//---------------------------------------------------------------------------
//	  EMGRobotics A2SCB and A2MCB driver class for Android
//    Written by Eric Gregori ( www.EMGRobotics.com )
//    Copyright (C) 2013  Eric Gregori
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
//	  This class supports version 2 of the EMGRobotics Audio Packet Protocol.
//    Any A2SCB or A2MCB on or after July 2013 supports this protocol.
//	  Header = 9000hz for 12ms
//	  Sync	 = 10000hz for 12ms
//	  Data   = 5000hz to 8000hz for 20ms
//---------------------------------------------------------------------------
package com.emgrobotics.autonomousvehicle;

import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.content.Context;



public class TDMAV2audiocontroller {

	private final double		HeaderDuration 	= 0.012;		// Header Duration
	private final int			HeaderFreq 		= 9000;			// Header Frequency
	private final double		SyncDuration 	= 0.012;		// Sync Duration
	private final int			SyncFreq 		= 10000;		// Sync Frequency
	private final double		DataDuration 	= 0.020;		// Data Duration
	private final double 		duration = HeaderDuration+DataDuration+SyncDuration+DataDuration+SyncDuration+DataDuration+SyncDuration+DataDuration; 
	private final double 		sampleRate 		= 44100.0;
	private final int 			numSamples 		= (int)(duration * sampleRate);
	private final double 		mLeftSamples[] 	= new double[numSamples];
	private final byte 			Direct[]   		= new byte[4 * numSamples];
	private final int			mMAXDataFreq 	= 7900;
	private final int			mMINDataFreq 	= 5100;
	private volatile boolean 	mThreadRunning 	= false;
	private volatile Thread 	mKeepRunning;
	private int					mServo1Freq; // 5100 to 7900
	private int					mServo2Freq;
	private int					mServo3Freq;
	private int					mServo4Freq;


	
	//------------------------------------------------------------------------------------------
	//
	// Public Interface
	//
	//------------------------------------------------------------------------------------------    
	public void Start()
	{
		InitToneGenerator();
	}
	
	
	public void setChannel( int channel, int position )
	{
		if( position > 180 || position < 0 ) return;
		float conversion = (mMAXDataFreq-mMINDataFreq)/180;
		int freq = mMINDataFreq + (int)(position * conversion);
		
		switch( channel)
		{
		case 1:
			mServo1Freq = freq;
			break;
		case 2:
			mServo2Freq = freq;
			break;
		case 3:
			mServo3Freq = freq;
			break;
		case 4:
			mServo4Freq = freq;
			break;
		default:
			break;
		}
	}
	
	
    public void stop() 
    {
    	mKeepRunning = null;
    }

	
	//------------------------------------------------------------------------------------------
	//
	// Private Methods
	//
	//------------------------------------------------------------------------------------------    
		
	//------------------------------------------------------------------------------------------
	//
	// Init Tone Generator
	//
	//------------------------------------------------------------------------------------------    
	private void InitToneGenerator()
	{
		// Init Tone Generator	
		
		int freq = (mMAXDataFreq-mMINDataFreq)/2;
		genToneSequence( freq, freq, freq, freq, Direct );

		// Set MAX volume
//		AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
//		audioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
//								audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
//								0);
								
		PlayStream();		// Kickoff the stream player
	}

	//------------------------------------------------------------------------------------------
	//
	//  Tone Generating Code
	//
	//------------------------------------------------------------------------------------------
	private void PlayStream()
	{
		if( mThreadRunning == true ) return;
	
		// Use a new tread as this can take a while
		final Thread thread = new Thread(new Runnable() 
		{
			public void run() 
			{
				AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
			            (int)sampleRate, AudioFormat.CHANNEL_OUT_STEREO,
			            AudioFormat.ENCODING_PCM_16BIT, 8*AudioTrack.getMinBufferSize ((int)sampleRate, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT),
			            AudioTrack.MODE_STREAM);    
				audioTrack.write(Direct, 0, Direct.length);
				audioTrack.play();	

				Thread thisThread = Thread.currentThread();
				mKeepRunning = thisThread;
		        while (mKeepRunning == thisThread)
				{   
					mThreadRunning = true;
					genToneSequence( mServo1Freq, mServo2Freq, mServo3Freq, mServo4Freq, Direct );
					audioTrack.write(Direct, 0, Direct.length);
				} // while - 06/26
    		    audioTrack.pause();
    		    audioTrack.flush();
    		    audioTrack.release();
		        
				mThreadRunning = false;

			} // run()
		}); // new thread
		thread.start();
	} // Playstream()

	private void genToneSequence( double Channel1, double Channel2, double Channel3, double Channel4, byte GeneratedSound[] )
	{
		// fill out the array
		int i,j;
		for (i = 0; i < (HeaderDuration*sampleRate); ++i)  							// Header
			mLeftSamples[i] = Math.sin(2 * Math.PI * i / (sampleRate/HeaderFreq));
		for( j=0; j<(DataDuration*sampleRate); ++i,++j) 							// Ch1
			mLeftSamples[i] = Math.sin(2 * Math.PI * i / (sampleRate/Channel1));
		for( j=0; j<(SyncDuration*sampleRate); ++i,++j) 							// Sync 
			mLeftSamples[i] = Math.sin(2 * Math.PI * i / (sampleRate/SyncFreq));
		for( j=0; j<(DataDuration*sampleRate); ++i,++j) 							// Ch2 
			mLeftSamples[i] = Math.sin(2 * Math.PI * i / (sampleRate/Channel2));
		for( j=0; j<(SyncDuration*sampleRate); ++i,++j) 							// Sync 
			mLeftSamples[i] = Math.sin(2 * Math.PI * i / (sampleRate/SyncFreq));
		for( j=0; j<(DataDuration*sampleRate); ++i,++j) 							// Ch3 
			mLeftSamples[i] = Math.sin(2 * Math.PI * i / (sampleRate/Channel3));
		for( j=0; j<(SyncDuration*sampleRate); ++i,++j) 							// Sync 
			mLeftSamples[i] = Math.sin(2 * Math.PI * i / (sampleRate/SyncFreq));
		for( ; i<numSamples; ++i) 													// Ch4 
			mLeftSamples[i] = Math.sin(2 * Math.PI * i / (sampleRate/Channel4));
        
		// convert to 16 bit pcm sound array
		// assumes the sample buffer is normalised.
		int idx = 0;
		short left, right;
		for (i = 0; i < (numSamples); ++i) 
		{
			// scale to maximum amplitude
			left = (short) ((mLeftSamples[i] * 32767));
			right = (short) ((mLeftSamples[i] * 32767));
    	
			// in 16 bit wav PCM, first byte is the low order byte - Stereo
			GeneratedSound[idx++] = (byte) (left & 0x00ff);
			GeneratedSound[idx++] = (byte) ((left & 0xff00) >>> 8);
			GeneratedSound[idx++] = (byte) (right & 0x00ff);
			GeneratedSound[idx++] = (byte) ((right & 0xff00) >>> 8);        		
		} // for
	}	


} // End of Class
